package gu.sql2java.wherehelper;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static gu.sql2java.utils.BaseTypeTransformer.asUnsignedLong;
import static gu.sql2java.utils.BaseTypeTransformer.INTERGRAL_CLASSES;
import static gu.sql2java.utils.ColumnTransformer.COLUMN_TRANSFORMER;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import com.google.common.base.Function;
import com.google.common.base.Throwables;

/**
 * 输入参数聚合为整型类型(Long|Integer|Short|Byte)的转换类
 * @author guyadong
 *
 * @param <T>
 * @since 3.27.0
 */
public class BitAggFun<T extends Number> implements Function<Object, T>{
	private final Class<T> type;
	@SuppressWarnings("rawtypes")
	private final Function<Collection, T> agg;
	/**
	 * @param type 数字类型(Long|Integer|Short|Byte)
	 * @param agg 将字符串集合聚合为整数的函数
	 */
	@SuppressWarnings({ "rawtypes" })
	public BitAggFun (Class<T>type,Function<Collection, T> agg) {
		this.type = checkNotNull(type,"type is null");
		checkArgument(INTERGRAL_CLASSES.contains(type),
				"Long , Integer, Short or Byte type required");
		this.agg = checkNotNull(agg,"agg is null");
	}
	@SuppressWarnings({ "rawtypes" })
	@Override
	public T apply(Object input) {
		if (null != input) {
			Long num = asUnsignedLong(input);
			if (null != num) {
				/** 输入的整型值转为无符号的long */
				return COLUMN_TRANSFORMER.to(num,Long.class, type);
			}
			if(input instanceof Enum) {
				try {
					input = nameOfEnum(input);
				} catch (Exception e) {
					Throwables.throwIfUnchecked(e);
					throw new RuntimeException(e);
				}
			}
			if(input instanceof String) {
				if(((String)input).matches("[+-]?\\d+")) {
					/** 字符串是否为数字格式则返回调用 valueOf方法将字符串转为数字 */
					try {
						return type.cast(type.getMethod("valueOf", String.class).invoke(null, input));
					} catch (Exception e) {
						Throwables.throwIfUnchecked(e);
						throw new RuntimeException(e);
					}
				}else {
					input = Collections.singleton(input);
				}
			}
			if(input instanceof String[]) {
				input = Arrays.asList((String[])input);
			}else if(input.getClass().isArray() && Enum.class.isAssignableFrom(input.getClass().getComponentType())) {
				ArrayList<String> list = new ArrayList<>();
				for(int i=0,end_i=Array.getLength(input);i<end_i;++i) {
					Object en = Array.get(input, i);
					if(null!= en) {
						list.add(nameOfEnum(en));
					}
				}
				input=list;
			}
			if(input instanceof Collection) {
				return agg.apply((Collection)input);
			}
		}
		return null;
	}
	
	private static String nameOfEnum(Object en) {
		try {
			return (String) en.getClass().getMethod("name").invoke(en);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
}

